#pragma once

/* wfspan层 */
// #define WFSPAN_FAST_BUT_MAYBUGGY	/* 可能修改用户数据 */

// #define WFSPAN_MBLOCK	(SIZE_2M)

#include <metalc/block/wfspan_obj.h>
#include <metalc/block/cobj.h>
/* 大型 lobj维护 */
OU2INFUN(cobj, wfspan_lobj)

SAI void _wfspan_pinit(const mlfunc_t * cont){
	PARAM_USED(cont);
	WFSPAN_PRINT("_wfspan pinit\n");
}

SAI void _wfspan_tinit(const mlfunc_t * cont, perm_allocf_t *alloc, wfspan_conf_t *conf){
	WFSPAN_PRINT("_wfspan tinit\n");
	mtls_t *tls = mtls_get();
	ASSERT(tls);
	// ASSERT(tls->objs[cont->lidx] == NULL_PTR);
	size_t sz = wfspanobj_size(conf->l1_clsnr, conf->l2_clsnr);
	void *mem = alloc(sz);
	ASSERT(mem);

	/* TODO: 解决重复初始化的问题 */
	if(!tls->objs[cont->lidx]) {
		tls->objs[cont->lidx] = wfspan_init(mem, sz, conf);
	}
	else{
		WFSPAN_PRINT("_wfspan tinit already %p\n", tls->objs[cont->lidx]);
	}
}
#ifdef WFSPAN_MBLOCK
SAI void *wfspan_mblock_oomhandler(wfspan_obj_t *l, mreq_t *req, size_t size, const mlfunc_t * cont, wfspan_conf_t *conf){
	void *ret = NULL_PTR;
	/* 申请ret */
	/* 向上申请? */
	req->size = WFSPAN_MBLOCK;
	req->align = WFSPAN_MBLOCK;
	void *mblk_ptr = cont->lalloc(req);

	WFSPAN_PRINT("[wfspan] oom alloc mblock from upper %p, size %lx\n", mblk_ptr, req->size);

	/* 初始化 */
	if (!mblk_ptr) return ret;
	
	ASSERT(is_ptr_aligned(mblk_ptr, WFSPAN_MBLOCK));
	mblock_t *blk = mblock_init(mblk_ptr, WFSPAN_MBLOCK, WFSPAN_MBLOCK/conf->mslab_size);
	/* 申请一定成功 */
	/* size alignup了吗? */
	ASSERT(is_aligned(size, conf->mslab_size));
	ret = mblock_alloc(blk, size, WFSPAN_MBLOCK, conf->mslab_size, WFSPAN_MBLOCK/conf->mslab_size);
	
	if (!mblock_is_empty(blk, WFSPAN_MBLOCK/conf->mslab_size)){
		/* 非空 */ 
		/* 插入队列末尾 */
		__dlist_insert(&l->mblk_list, &blk->node, &blk->node, l->mblk_list.prev);
	}
	else{
		/* 提交 */
		mblock_discard(blk, WFSPAN_MBLOCK/conf->mslab_size);
	}

	// if(!ret) WFSPAN_PRINT("[wfspan] mblock alloc %p, size %lx\n", ret, size);
	ASSERT(ret);
	WFSPAN_PRINT("[wfspan] mblock alloc %p, size %lx\n", ret, size);
	// WFSPAN_PRINT("[wfspan] mblock alloc %p, size %lx\n", ret, size);
	ASSERT(is_ptr_aligned(ret, conf->mslab_size));
	ASSERT(is_ptr_inbound(ret, blk, (paddr_t)blk + WFSPAN_MBLOCK - conf->mslab_size));
	return ret;
}

/* 顺序应该是 local -> full -> mblock -> mblock */

/* define wfspan */
SAI void *wfspan_try_mblock_alloc(wfspan_obj_t *l, size_t size, wfspan_conf_t *conf){
	void *ret = NULL_PTR;
	/* 满足的最大大小 */
	ASSERT (size <= WFSPAN_MBLOCK - conf->mslab_size);
	/* 看block是否满足 */
	// return NULL_PTR;
	if (!list_is_empty(&l->mblk_list)){
		/* 从blk里面申请 */
		mblock_t *blk = list_entry(mblock_t, l->mblk_list.next, node);
		/* size 对齐 */
		ret = mblock_alloc(blk, size, WFSPAN_MBLOCK, conf->mslab_size, WFSPAN_MBLOCK/conf->mslab_size);
		if (ret){
			if (mblock_is_empty(blk, WFSPAN_MBLOCK/conf->mslab_size)){
				/* 空了, remove*/
				dlist_del(&l->mblk_list, blk->node.next);
				/* 提交 */
				mblock_discard(blk, WFSPAN_MBLOCK/conf->mslab_size);
			}
				/* 申请成功，返回 */
			ASSERT(is_ptr_aligned(ret, conf->mslab_size));
			ASSERT(is_ptr_inbound(ret, blk, (paddr_t)blk + WFSPAN_MBLOCK - conf->mslab_size));
			WFSPAN_PRINT("mblock alloc %p from %p\n", ret, blk);
			return ret;
		}
			/* 申请失败，空间不够，向上再次申请 */
	}
	return ret;
}
#endif

SAI void *wfslab_oomhandler(wfspan_obj_t *l, mreq_t *req, size_t size, const mlfunc_t * cont, wfspan_conf_t *conf){
	void *ret = NULL_PTR;
	PARAM_USED(l);
#ifdef WFSPAN_MBLOCK
	/* 内存不足时优先在本地mblock中满足 */
	if (size <= WFSPAN_MBLOCK - conf->mslab_size){
		ret = wfspan_try_mblock_alloc(l, size, conf);
		if (!ret) ret = wfspan_mblock_oomhandler(l, req, size, cont, conf);

		/* 设置meta信息，free时也要设置，否则会出现问题 */
		WFSPAN_PRINT("[wfmblock] alloc %p, size %lx\n", ret, size);
		req->size = size;
		req->align = conf->mslab_size;
		return ret;
	}
	
#endif
	/* 大于则直接申请 */

	req->size = size;
	req->align = req->align < conf->mslab_size ? conf->mslab_size : req->align;
	ret = cont->lalloc(req);
	WFSPAN_PRINT("[wfspan] oom alloc from upper %p, size %lx\n", ret, req->size);


	return ret;
}

SAI void *wfspan_l2_alloc(mreq_t *req, mtls_t *tls, const mlfunc_t * cont, wfspan_obj_t *l, size_t req_size, wfspan_conf_t *conf, INFUN(wfspan_clspol, l2clspol), INFUN(wfspan_lobj, lobj)){
	void *ret;

	
	szcls_t cls = l2clspol->sz2cls(req_size) + conf->l1_clsnr;
	size_t aligned_size = l2clspol->cls2sz(cls - conf->l1_clsnr);
	mslab_t *l2block = NULL_PTR;
			
	WFSPAN_PRINT("[wfspan l2] req_size %lx, szcls %u, l2szcls %u, aligend_size %lx\n", req_size, cls, cls - conf->l1_clsnr, aligned_size);
	WFSPAN_PRINT("[wfspan l2] req_size %lx, szcls %u, l2szcls %u, aligend_size %lx\n", req_size, cls, cls - conf->l1_clsnr, aligned_size);
	ASSERT(aligned_size >= req_size);
	/* 尝试窃取 */
#if  !defined (WFSPAN_FAST_BUT_MAYBUGGY) && defined(WFSPAN_MBLOCK)  	/* 可能修改用户数据 */
	if (aligned_size <= WFSPAN_MBLOCK - conf->mslab_size){
		/* 应该可以窃取??是不是meta信息位置有错 */
		l2block = wfspan_lockfree_fastpath(l, cls, 1, conf);	/* 可以无锁窃取*/
		ASSERT(!l2block || !is_ptr_aligned(l2block, WFSPAN_MBLOCK));
		if (!l2block) l2block = wfspan_waitfree_acquire(l, cls, 0, conf, tls->apdstls.tinfo.cid, tls->apdstls.tinfo.tid);	/* 从自己开始满足 */
	}
	else{
		l2block = wfspan_lockfree_fastpath(l, cls, 0, conf);	/* 不能无锁窃取，会有修改用户数据的可能性 */
		ASSERT(!l2block ||is_ptr_aligned(l2block, WFSPAN_MBLOCK));
		if (!l2block) l2block = wfspan_waitfree_acquire(l, cls, 1, conf, tls->apdstls.tinfo.cid, tls->apdstls.tinfo.tid);	/* 从自己开始满足 */
	}
#else
	l2block = wfspan_lockfree_fastpath(l, cls, 1, conf);	 	/* 不安全!! */
	if (!l2block) l2block = wfspan_waitfree_acquire(l, cls, 0, conf, tls->apdstls.tinfo.cid, tls->apdstls.tinfo.tid);	
#endif

#ifdef WFSPAN_MBLOCK
	if (l2block && (aligned_size <= WFSPAN_MBLOCK - conf->mslab_size)){
		/*size <= wfspan_block 申请到的是meta节点,需要转换 */
		mslab_t *cur_meta = (mslab_t *)l2block;
		l2block = mslab_usable_in_mblock(cur_meta, WFSPAN_MBLOCK, conf->mslab_size);
		cur_meta->cls = cls;
		cur_meta->unit_size = aligned_size;
		// ASSERT(mslab_by_ptr_from_mblock(l2block, WFSPAN_MBLOCK, conf->mslab_size) == cur_meta);
		ASSERT(!is_ptr_aligned(l2block, WFSPAN_MBLOCK));
		// ASSERT((paddr_t)l2block >= ((paddr_t)mblock_by_ptr(l2block, WFSPAN_MBLOCK) + conf->mslab_size));
	}
#endif		
	/* 到这已经是用户可使用部分 */
	/* l2block */
	if (!l2block) {
		/* 向上申请 */
		l2block = (mslab_t *)wfslab_oomhandler(l, req, aligned_size, cont, conf);
		/* TODO: 设置cobj */
		if (!l2block) return NULL_PTR;
#ifdef WFSPAN_MBLOCK
		/* 这里申请出来两种可能，一种是block申请，需要填入meta信息 */
		if (aligned_size <= WFSPAN_MBLOCK - conf->mslab_size){
			ASSERT(!is_ptr_aligned(l2block, WFSPAN_MBLOCK));
			/* 设置meta信息 */
			mslab_t *cur_meta = mslab_by_ptr_from_mblock(l2block, WFSPAN_MBLOCK, conf->mslab_size);
			// ASSERT(mslab_usable_in_mblock(cur_meta, WFSPAN_MBLOCK, conf->mslab_size) == l2block);
			cur_meta->cls = cls;
			cur_meta->unit_size = aligned_size;
		}
		else{
			/* 只有大于mblock能分配的大小时才设置 */
			ASSERT (aligned_size > WFSPAN_MBLOCK - conf->mslab_size);
			ASSERT(is_ptr_aligned(l2block, WFSPAN_MBLOCK));
			lobj->set_cobj(l2block, aligned_size, 1);
		}
		
#else
		lobj->set_cobj(l2block, aligned_size, 1);
#endif
	}
	/* 自对齐 */
	ret = l2block;
	req->ptr = l2block;
	req->size = aligned_size;
	req->nr = 1;
	WFSPAN_PRINT("[wfspan l2] "PRIN_RED() "alloc " PRIN_DFT()" %p, size %lx, cls %u\n", ret, aligned_size, cls);
	WFSPAN_PRINT("[wfspan l2] "PRIN_RED() "alloc " PRIN_DFT()" %p, size %lx, cls %u\n", ret, aligned_size, cls);
	// ASSERT(is_ptr_aligned(l2block, aligned_size));
	return ret;	
}

SAI void *_wfspan_alloc(mreq_t *req, const mlfunc_t * cont, cls2maxnr * const u2mnr, wfspan_conf_t *conf, \
INFUN(wfspan_clspol, clspol), INFUN(wfspan_clspol, l2clspol), INFUN(wfspan_lobj, lobj)){
	size_t req_size = req->size;
	/* 自对齐方式 */
	if (UNLIKELY(req->align != 0))
		req_size = align_up(req_size, req->align);

	/* 允许申请32768 */
	if (__MLAYER_INVALID_REQSZ(cont->lmaxsize, req_size)) return cont->lalloc(req);
	// if (__MLAYER_INVALID_REQSZ(conf->max_sz, req_size)) return cont->lalloc(req);

	void *ret;
	void *upper_mem = NULL_PTR;
	mtls_t *tls = mtls_by_req(req);
	wfspan_obj_t *l = (wfspan_obj_t *)MLAYER_GET_OBJ(tls, cont);
	ASSERT(l);


	if (l2clspol && UNLIKELY(req_size > conf->l1_maxsz)) {
		/* 大块内存向上申请 > 32768  */
		return wfspan_l2_alloc(req, tls, cont, l, req_size, conf, l2clspol, lobj);
	}

	mslab_t *cur_slab = NULL_PTR;
	szcls_t cls = clspol->sz2cls(req_size);
	hword_t unit_size = (hword_t)clspol->cls2sz(cls);
	hword_t max_nr = u2mnr(cls);
	WFSPAN_PRINT("[wfspan] try alloc, need size %lx, utsz %x, cls %u\n", req_size, unit_size, cls);
	wfspan_cls_t *szclsp = wfspanobj_get_clsarr(l);
	
	/* 先尝试本地申请 */
	ret = wfspan_local_alloc(l, szclsp, cls, unit_size, conf);
	if (LIKELY(ret)) goto FIN; 

#ifndef WFSPAN_MBLOCK
	/* 优先从本地full_list中满足获取可以降低WCET */
	ret = wfspan_local_full_alloc(l, szclsp, cls, unit_size, max_nr, conf);
	if (LIKELY(ret)) goto FIN;
#endif


	/* 本地申请失败，尝试窃取 */
	ret = wfspan_acquire_from_others(l, wfspan_lockfree_fastpath(l, cls, 1, conf), szclsp, cls, unit_size, conf, tls->apdstls.tinfo.cid, tls->apdstls.tinfo.tid);
	if (LIKELY(ret)) goto FIN;
	ret = wfspan_acquire_from_others(l, NULL_PTR, szclsp, cls, unit_size, conf, tls->apdstls.tinfo.cid, tls->apdstls.tinfo.tid);
	if (LIKELY(ret)) goto FIN;

	/* 窃取失败，继续向上申请 */
	if (l2clspol){
		/* 向l2窃取一个64KB slab */
		szcls_t spcls = conf->l1_clsnr;
	#if  !defined(WFSPAN_MBLOCK) && !defined(WFSPAN_FAST_BUT_MAYBUGGY)			
		upper_mem = wfspan_lockfree_fastpath(l, spcls, 0, conf);
		if (!upper_mem) upper_mem = wfspan_waitfree_acquire(l, spcls, 0, conf, tls->apdstls.tinfo.cid, tls->apdstls.tinfo.tid);
	#else
		/* WFSPAN_MBLOCK这样窃取是安全的 */
		upper_mem = wfspan_lockfree_fastpath(l, spcls, 1, conf);		/* 不安全!! 可能修改用户数据 */
		if (!upper_mem) upper_mem = wfspan_waitfree_acquire(l, spcls, 0, conf, tls->apdstls.tinfo.cid, tls->apdstls.tinfo.tid); 
	#endif
		/* TODO: 64Kb slab需要转换*/
#ifdef	WFSPAN_MBLOCK 
		/* 这个是64KB，一定需要转换成指针形式 */
		ASSERT(!upper_mem || !is_ptr_aligned(upper_mem, conf->mslab_size));
		if (upper_mem) upper_mem = mslab_usable_in_mblock((mslab_t *)upper_mem, WFSPAN_MBLOCK, conf->mslab_size);
		ASSERT(!upper_mem || is_ptr_aligned(upper_mem, conf->mslab_size));
#endif
		WFSPAN_PRINT("[wfspan l2] acquire %p, cls %u, size %lx\n", upper_mem, spcls, l2clspol->cls2sz(spcls - conf->l1_clsnr));
		WFSPAN_PRINT("[wfspan l2] acquire %p, cls %u, size %lx\n", upper_mem, spcls, l2clspol->cls2sz(spcls - conf->l1_clsnr));
	}

	if (UNLIKELY(!upper_mem)) {
		upper_mem = wfslab_oomhandler(l, req, conf->mslab_size, cont, conf);
		// ASSERT(!is_ptr_inbound(upper_mem, (paddr_t)mblock_by_ptr(upper_mem, WFSPAN_MBLOCK), (paddr_t)mblock_by_ptr(upper_mem, WFSPAN_MBLOCK) + conf->mslab_size - 1));
	}

	if (LIKELY(upper_mem)){
		/* 申请成功 */
		ret = wfslab_local_init_alloc(l, upper_mem, conf->mslab_size, szclsp, cls, unit_size, max_nr);
	}
FIN:	
	
	// ASSERT(!is_ptr_aligned(ret, WFSPAN_MBLOCK));
	// ASSERT(!is_ptr_inbound(ret, (paddr_t)mblock_by_ptr(ret, WFSPAN_MBLOCK), (paddr_t)mblock_by_ptr(ret, WFSPAN_MBLOCK) + conf->mslab_size - 1));
	WFSPAN_PRINT("[wfspan] " PRIN_RED() "alloc " PRIN_DFT() " %p, need size %lx, real size %x, cls %u\n", ret, req_size, unit_size, cls);
	ASSERT(ret);
	ASSERT(unit_size >= req_size);
	req->size = (size_t)unit_size;
	req->ptr = (void *)ret;
	req->nr = 1;
	return ret;
}

/* 下面都是free */
SAI size_t wfspan_local_free(wfspan_obj_t *l, mslab_t *s, void *free_ptr, szcls_t cls, sword_t slab_max_nr, dlist_t **phead, dlist_t **ptail, wfspan_conf_t *conf){	
	PARAM_USED(cls);
	/* 本地释放路径 */
	sword_t consumed_cnt = mslab_local_free(s, (mlistnode_t *)free_ptr, (mlistnode_t *)free_ptr, 1);
	WFSPAN_PRINT("[wfspan] local free %p in [%p], cur local cnt %ld, max nr %ld\n", free_ptr, s, consumed_cnt, mslab_visible_nr(s));
			
	/* 本地释放应该保留? */
	/* 需要在本地释放路径判断全局大小吗? */
	if ((consumed_cnt >= 0) &&
		/*(mslab_visible_nr(s) + (sword_t)consumed_cnt == slab_max_nr)*/
		__mslab_is_full(s, slab_max_nr)){
		
		/* TODO:似乎可以不放在头部，而是直接留用，在被RCU换出去的时候再检查是否满，这样可以避免 */
		/* 释放节点 */
		wfspan_remove_local_list(l, s);
#ifndef WFSPAN_MBLOCK
		/* 其实是L2启用都可以这样 */
		/* 优先放到本地full_list的头部 */
		wfspan_push_full_local_list(l, cls, s);

		WFSPAN_PRINT("[wfspan] free slab %p to local full list\n", s);
		/* 检查是否出队 */
		/* 是否需要出队? 必须需要吧 */
		if (l->slab_nr >= conf->slab_limits) {
			/* 满的出队 */
			// return wfspan_clear_full_local_list(l, phead, ptail);
			
			/* 释放1个 */
			mslab_t *retnode = wfspan_pop_tail_full_local_list(l);
			*phead = &retnode->node; *ptail = &retnode->node;
			return 1;
		}
		ASSERT(l->full_nr >= 1 && l->full_nr <= conf->slab_limits && l->full_nr <= l->slab_nr);
#else
		PARAM_USED(conf);
		/* 直接出队试试?这样full alloc步骤也省了 */
		mslab_t *retnode = s;
		*phead = &retnode->node; *ptail = &retnode->node;
		return 1;
#endif
	}
	return 0;
}
SAI size_t wfspan_remote_free(wfspan_obj_t *l, mslab_t *s, void *free_ptr, szcls_t cls, sword_t slab_max_nr, dlist_t **phead, dlist_t **ptail, wfspan_conf_t *conf){
	PARAM_USED(slab_max_nr);
	size_t ret_nr = 0;
	/* 使用dlist出队一定要确保node是第一个成员 */
	ASSERT(OFFSETOF(mslab_t, node) == 0);
	wfspan_cls_t *szclsp = wfspanobj_get_clsarr(l);
	// size_t slab_nr = l->slab_nr;

	WFSPAN_PRINT("[wfspan] remote free %p in [%p]\n", free_ptr, s);
	/* 远程释放 */
	sword_t cnt = mslab_remote_free(s, (mlistnode_t *)free_ptr, (mlistnode_t *)free_ptr, 1);
	/* cnt == lmax_nr 应该madvise提交 */
	if (LIKELY(cnt != 0))
		return 0;
	/* 等于0则负责回收 */
	WFSPAN_PRINT("[wfspan] reclaim [%p]\n", s);
	mlistnode_t *reclaimed_list = mlist_hd_exchg(&s->rlist, NULL);
	atm_st(&s->allocator, l);
	/* 立马修改，减少cache冲突 */
	ASSERT(reclaimed_list != NULL_PTR);
	s->flist = reclaimed_list;

	/* 设置所有者 */
	/* 插入到尾部等待复用 */
	wfspan_push_local_list_tail(l, szclsp, s, cls);
	/* 插入到头部等待复用 */
	// wfspan_push_local_list_head(l, szclsp, s, cls);

	ASSERT(s->cls == cls);
	// TODO: 最近最少使用的出队
	if (l->slab_nr >= conf->slab_limits)
	{
		/* 大于本地限制则出队 */
		/* 优先出队本地已满的 */
	#ifndef WFSPAN_MBLOCK
		if (l->full_nr != 0) {
			/* 出队已满的最后一个 */
			// ret_nr = wfspan_clear_full_local_list(l, phead, ptail);

			/* 释放一个 */
			mslab_t *retnode = wfspan_pop_tail_full_local_list(l);
			*phead = &retnode->node; *ptail = &retnode->node;
			ret_nr = 1;
		}
		else 
	#endif
		{
			
			ASSERT(!list_is_empty(&l->non_full_lru_list));
			/* 出队 */
			mslab_t *rep = wfspan_pop_recently_unused(l);
			
		#ifndef WFSPAN_CLEAN_SPMCLIST
			/* 检查是否满 */
			if (mslab_is_full(rep)){
				// wfspan_push_full_local_list(l, rep);
				// ret_nr = wfspan_clear_full_local_list(l, phead, ptail);

				/* 真正释放 */
				// rep->unit_size = conf->mslab_size;
				/* 释放1个 */
				mslab_t *retnode = rep;
				*phead = &retnode->node; *ptail = &retnode->node;
				ret_nr = 1;
			}else{
				/* 塞回去 */
				
				szcls_t repcls = rep->cls;
				rep->allocator = NULL_PTR;
				wfspan_researved_mslabn(l, repcls, rep, rep);
			}
			
			INFO("[wfspan] insert recently unused [%p] to spmc list\n", rep);
		#else
			// rep->allocator = NULL_PTR;
			/* 下面这种方式 可能会导致无上界的内存占用 */
			// /* 使用该节点清理队列 */
			// rep->node.next = NULL_PTR;
			// /* TODO: 增加一个限制n，以限制回收步骤 */
			// // ret_nr = wfspan_clean_spmclists(l, repcls, (spmcnode_t *)rep, free_slab_nr, conf);
			// /* 只瞥4个 */
			ret_nr = wfspan_clean_spmclists_limit(l, repcls, (spmcnode_t *)&rep->node, phead, ptail, 4, conf);
		#endif
		}
	}

	WFSPAN_PRINT("[wfspan] reclaim [%p] to local list\n", s);

	return ret_nr;
}
/* TODO: 查找Allocator的时候也可以通过本地索引判断？这样也能减少Cache行的竞争 */


SAI bool_t wfspan_l2_free(wfspan_obj_t *l, mreq_t *req, wfspan_conf_t *conf, INFUN(wfspan_clspol, l2clspol), INFUN(wfspan_lobj, lobj)){
	
	szcls_t cls = l2clspol->sz2cls(req->size) + conf->l1_clsnr;
	mslab_t *cur_slab;
	WFSPAN_PRINT("[wfspan l2] try free %p, size %lx, cls %u\n", req->ptr, req->size, cls);
#ifdef	WFSPAN_MBLOCK 
	/* 需要转换 */
	if (!is_ptr_aligned(req->ptr, WFSPAN_MBLOCK)){
		ASSERT(req->size <= WFSPAN_MBLOCK - conf->mslab_size);
		/* 大小小于，填入cur_slab */
		ASSERT(is_aligned(req->size, conf->mslab_size));
		cur_slab = mslab_by_ptr_from_mblock(req->ptr, WFSPAN_MBLOCK, conf->mslab_size);
		// ASSERT(mslab_usable_in_mblock(cur_slab, WFSPAN_MBLOCK, conf->mslab_size) == req->ptr);
#ifdef WFSPAN_MADVISE_FREE
		/* 可以全部释放 */
		// madvise(req->ptr, req->size, WFSPAN_MADVISE_FREE);
#endif

	}
	else
#endif
	{	
		cur_slab = (mslab_t *)lobj->uptr2ptr(req->ptr);
	#ifdef WFSPAN_MADVISE_FREE
		/* 只能部分释放 */
		madvise((void *)((paddr_t)req->ptr + PAGE_SIZE), req->size - PAGE_SIZE, WFSPAN_MADVISE_FREE);
	#endif
	}
	WFSPAN_PRINT("[wfspan l2]" PRIN_YELLOW() " free" PRIN_DFT()" %p slab %p, size %lx, cls %u\n", req->ptr, cur_slab, req->size, cls);
	/* 不做检查 */
	/* > WFSPAN MBLOCK的大小需要以头部入队，否则以block中的Meta指针入队 */

	__wfspan_researved_mslabn(l, cls, cur_slab, cur_slab);
	return TRUE;
}

SAI bool_t wfslab_oofhandler(mreq_t *req, dlist_t *head, dlist_t *tail, size_t nr, const mlfunc_t * cont, wfspan_conf_t *conf)
{
	INFO("[wfspan] oof free slab %p - %p, nr %lx, ptr %p, size %lx\n", head, tail, nr, req->ptr, conf->mslab_size);
	req->ptr = head;
	req->sptr = tail;
	req->size = conf->mslab_size;
	req->nr = nr;
	return cont->lfree(req);
}

/* 优化方案1， max_nr和owner_tid编码 */
SAI bool_t _wfspan_free(mreq_t *req, const mlfunc_t * cont, cls2maxnr * const u2mnr, wfspan_conf_t *conf, \
INFUN(wfspan_clspol, clspol), INFUN(wfspan_clspol, l2clspol), INFUN(wfspan_lobj, lobj)){
	size_t get_size = req->size;

	/* 允许申请32768 */
	// if (__MLAYER_INVALID_REQSZ(conf->max_sz, get_size)) return cont->lfree(req);
	if (__MLAYER_INVALID_REQSZ(cont->lmaxsize, get_size)) return cont->lfree(req);
	mtls_t *tls = mtls_by_req(req);
	wfspan_obj_t *l = (wfspan_obj_t *)MLAYER_GET_OBJ(tls, cont);
	ASSERT(l);
	/* 启用l2时的释放路径 > 32768 */
	if (l2clspol && UNLIKELY(get_size > conf->l1_maxsz)) {
		/* 直接插入到spmc链表中 */
		return wfspan_l2_free(l, req, conf, l2clspol, lobj);
	}

	/* wfslab释放 */
#ifdef WFSPAN_MBLOCK
	mslab_t *s = mslab_by_ptr_from_mblock(req->ptr, WFSPAN_MBLOCK, conf->mslab_size);
#else
	mslab_t *s = mslab_byptr(req->ptr, conf->mslab_size);
#endif
	/* 提前读取 */
	wfspan_obj_t *r = (wfspan_obj_t *)atm_ld(&s->allocator);
	

	// 
	PARAM_USED(clspol);
	szcls_t cls = req->cls;
	hword_t max_nr = u2mnr(cls);
	WFSPAN_PRINT("[wfspan] try free %p, size %lx, cls %u, slab %p\n", req->ptr, req->size, cls, s);
	/* cid_t tid = tls->apdstls.tWFSPAN_PRINT.tid; */
	ASSERT(req->nr == 1);
	
	ASSERT(s->cls == cls);

	/* 释放 */
	size_t ret_nr;
	dlist_t *ret_head;
	dlist_t *ret_tail;
	if (r == l){
		/* 本地释放 */
		ret_nr = wfspan_local_free(l, s, req->ptr, cls, (sword_t)max_nr, &ret_head, &ret_tail, conf);
	}else{
		ret_nr = wfspan_remote_free(l, s, req->ptr, cls, (sword_t)max_nr, &ret_head, &ret_tail, conf);
	}
	/* 释放后需要向上层释放 */
	if (UNLIKELY(ret_nr)) {
		ASSERT(ret_head && ret_tail);
		
		if (l2clspol) {
			/* slab块向自己释放 */
			szcls_t pcls = conf->l1_clsnr;
			WFSPAN_PRINT("[wfspan l2] "PRIN_YELLOW() "free slab" PRIN_DFT()" %p - %p, size %lx, cls %u\n", ret_head, ret_tail, get_size, cls);
			/* 不做检查 */
			
#ifdef WFSPAN_MADVISE_FREE
			/* 可以全部释放 */
			/* 关键方法是合并一起释放 */
			/* 但问题还是释放太多了*/
			ASSERT((ret_nr == 1) && (ret_head == ret_tail));
			// void *ptr = mslab_usable_in_mblock((mslab_t *)ret_head, WFSPAN_MBLOCK, conf->mslab_size);
			// madvise(ptr, conf->mslab_size, WFSPAN_MADVISE_FREE);
#endif
			__wfspan_researved_mslabn(l, pcls, (mslab_t *)ret_head, (mslab_t *)ret_tail);
			return TRUE;
		}
		else{
	#ifdef WFSPAN_MBLOCK
			/* 要转换 */
			dlist_t new_list;
			dlist_init(&new_list);
			dlist_t *cur = ret_head;
			dlist_t *tail = ret_tail;
			dlist_t *next;
			tail->next = NULL_PTR;
			do {
				next = cur->next;
				dlist_t *real_ptr = mslab_usable_in_mblock((mslab_t *)cur, WFSPAN_MBLOCK, conf->mslab_size);
				dlist_add_tail(&new_list, real_ptr);
				cur = next;
			} while(cur);
			ret_head = new_list.next;
			ret_tail = new_list.prev;
	#endif
			// ASSERT(slist_len_noloop(ret) == free_slab_nr);
			return wfslab_oofhandler(req, ret_head, ret_tail, ret_nr, cont, conf);
		}
	}
	WFSPAN_PRINT("[wfspan] "PRIN_YELLOW() "free" PRIN_DFT() "%p, size %lx, cls %u, slab %p\n", req->ptr, req->size, cls, s);
	return TRUE;
}

/* 窃取(cache不友好但有助于减少内存占用) vs 帮助入队(cache友好但是会导致问题) */
/* TODO优化点 */
/* TODO:回收span后优先交还给拥有线程，只有拥有线程竞争激烈时才会自己占有 */
/* 只有free过的线程才会被加入窃取队列?否则一定为空 */
/* 定期清理链表?(谁来清理?如何保证内存安全?) */

SAI size_t _wfspan_sizeof(mreq_t *req, const mlfunc_t * cont, wfspan_conf_t *conf, INFUN(wfspan_clspol, l2clspol), INFUN(wfspan_lobj, lobj)){
	ASSERT(req->ptr);
#ifdef WFSPAN_MBLOCK
	const size_t align_size = WFSPAN_MBLOCK;
#else
	const size_t align_size = conf->mslab_size;
#endif
	/* 如果不按wfspan_block_size对齐就是本层管理的  */
	if (LIKELY(!is_ptr_aligned(req->ptr, align_size))){
		/* 对齐后也有两种情况 */
		/* 一种对齐 */
		mslab_t *s;

	#ifdef WFSPAN_MBLOCK
		s = mslab_by_ptr_from_mblock(req->ptr, WFSPAN_MBLOCK, conf->mslab_size);
	#else
		s = mslab_byptr(req->ptr, conf->mslab_size);
	#endif
		word_t cls_size = s->cls_size;
		szcls_t cls = word_high(cls_size);
		hword_t unit_size = word_low(cls_size);
		WFSPAN_PRINT("[wfspan] sizeof %p, sz %x, cls %u\n", req->ptr, unit_size, cls);
		req->size = unit_size;
		req->cls = cls;
		/* WFSPAN_PREFETCH(req->ptr); */ /* 提前预取，因为插入队列一定需要访问 */
		return req->size;
		/* 对齐且存在小的情况? */
	} else if (l2clspol) {
		/* 对齐且存在大的 */
		void *ptr = lobj->uptr2ptr(req->ptr);
		size_t size = lobj->ptr_getsize(ptr);
		req->ptr = ptr;
		req->size = size;
		WFSPAN_PRINT("[wfspan l2] sizeof %p, sz %lx\n", req->ptr, size);
		return size - lobj->mocupysz();
	}
	return cont->lsizeof(req);
}

/* 定义 
 * min_bit - 管理的最小bit位
 * max_bit - 管理的最大bit位
 * slab_size - slab大小
 * slab_limit - 线程持有的slab个数限制，推荐为41
 * p_frac - travel的分数，推荐为0，越大内存占用越高，性能越好
 * help_quota - 单次帮助数量，推荐为1
 * cls_comp - szcls策略
 * u2mfunc - unit_size到max_nr的映射函数
 * perm_alloc - 永久对象分配函数
*/
#define DEF_WFSLAB_MLAYER(name, min_bit, max_bit, slab_size, slab_limit, p_frac, help_quota, cls_comp, u2mfunc, perm_alloc)\
DEF_MLAYER_BASIC(name, BIT(max_bit), 1)\
static const wfspan_conf_t name##_conf = { .mslab_size = slab_size, .slab_limits = slab_limit, .travel_frac = p_frac, \
.max_help_quota = help_quota, .max_sz = _maxsz_##cls_comp, .l1_minsz = BIT(min_bit), .l1_maxsz = _maxsz_##cls_comp, \
.l2_minsz = 0, .l2_maxsz = 0, .l1_clsnr = _clsnr_##cls_comp + 1, .l2_clsnr = 0 }; \
SAI void name##_pinit(const mlfunc_t * cont){ return _wfspan_pinit(cont); }\
SAI void name##_tinit(const mlfunc_t * cont){ return _wfspan_tinit(cont, perm_alloc, &name##_conf); }\
SAI void *name##_alloc(mreq_t *req, const mlfunc_t * cont){\
	return _wfspan_alloc(req, cont, u2mfunc, &name##_conf, OUFUN_OF(cls_comp), NULL_PTR, NULL_PTR);\
}\
SAI bool_t name##_free(mreq_t *req, const mlfunc_t * cont){\
	return _wfspan_free(req, cont, u2mfunc, &name##_conf, OUFUN_OF(cls_comp), NULL_PTR, NULL_PTR);\
}\
SAI size_t name##_sizeof(mreq_t *req, const mlfunc_t * cont){\
	return _wfspan_sizeof(req, cont, &name##_conf, NULL_PTR, NULL_PTR); }


/* 增加2级 */
#define DEF_WFSPAN_MLAYER(name, min_bit, max_bit, slab_size, slab_limit, p_frac, help_quota, cls_comp, u2mfunc, perm_alloc, l2_max_bit, os_alloc)\
DEF_MLAYER_BASIC(name, BIT(max_bit + l2_max_bit), 1)\
DEF_CLS_POW2B(name##_2b, max_bit + 1, HDEF_PTR_BITS)\
static cobj_ord_t *pext_##name;\
COBJ_EXT(ext_##name, pext_##name, slab_size)\
static const wfspan_conf_t name##_conf = { .mslab_size = slab_size, .slab_limits = slab_limit, .travel_frac = p_frac, \
.max_help_quota = help_quota, .max_sz = _maxsz_##name##_2b, .l1_minsz = BIT(min_bit), .l1_maxsz = BIT(15), \
.l2_minsz = BIT(max_bit + 1), .l2_maxsz = _maxsz_##name##_2b, .l1_clsnr = _clsnr_##cls_comp + 1, .l2_clsnr = _clsnr_##name##_2b }; \
SAI void name##_pinit(const mlfunc_t * cont){ if(!pext_##name){ \
	pext_##name = os_alloc(BIT(HDEF_PTR_BITS)/slab_size);\
	}\
	return _wfspan_pinit(cont); }\
SAI void name##_tinit(const mlfunc_t * cont){ return _wfspan_tinit(cont, perm_alloc, &name##_conf); }\
SAI void *name##_alloc(mreq_t *req, const mlfunc_t * cont){\
	return _wfspan_alloc(req, cont, u2mfunc, &name##_conf, OUFUN_OF(cls_comp), OUFUN_OF(name##_2b), OUFUN_OF(ext_##name));\
}\
SAI bool_t name##_free(mreq_t *req, const mlfunc_t * cont){\
	return _wfspan_free(req, cont, u2mfunc, &name##_conf, OUFUN_OF(cls_comp), OUFUN_OF(name##_2b), OUFUN_OF(ext_##name));\
}\
SAI size_t name##_sizeof(mreq_t *req, const mlfunc_t * cont){\
	return _wfspan_sizeof(req, cont, &name##_conf, OUFUN_OF(name##_2b), OUFUN_OF(ext_##name)); }
